package com.smartbus.bus.service.impl;

import com.smartbus.bus.service.TextToSpeechService;
import com.smartbus.bus.service.IVoiceFilesService;
import com.smartbus.bus.domain.VoiceFiles;
import com.smartbus.common.config.RuoYiConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 百度语音合成服务实现类
 *
 * @author smartbus
 * @date 2024-09-24
 */
@Service
public class TextToSpeechServiceImpl implements TextToSpeechService {

    @Value("${baidu.tts.api-key:}")
    private String apiKey;

    @Value("${baidu.tts.secret-key:}")
    private String secretKey;

    @Value("${baidu.tts.enabled:false}")
    private boolean ttsEnabled;

    @Autowired
    private IVoiceFilesService voiceFilesService;

    // Access Token缓存
    private static final Map<String, String> tokenCache = new ConcurrentHashMap<>();
    private static final Map<String, Long> tokenExpireCache = new ConcurrentHashMap<>();

    // 百度TTS API地址
    private static final String TTS_API_URL = "https://tsn.baidu.com/text2audio";
    private static final String TOKEN_API_URL = "https://aip.baidubce.com/oauth/2.0/token";

    // 语音文件存储路径
    private static final String VOICE_DIR = "/voice/";

    @Override
    public String textToSpeech(String text, String voiceType, Integer speed, Integer pitch, Integer volume) {
        if (!ttsEnabled || text == null || text.trim().isEmpty()) {
            return null;
        }

        try {
            // 检查文本长度
            if (text.getBytes(StandardCharsets.UTF_8).length > 1024) {
                throw new RuntimeException("文本长度不能超过1024字节");
            }

            // 获取access token
            String accessToken = getAccessToken();
            if (accessToken == null) {
                throw new RuntimeException("获取百度API访问令牌失败");
            }

            // 生成文件名
            String fileName = generateFileName(text, voiceType, speed, pitch, volume);
            String relativePath = VOICE_DIR + fileName;
            String fullPath = RuoYiConfig.getUploadPath() + relativePath;

            // 检查文件是否已存在
            File file = new File(fullPath);
            if (file.exists()) {
                return relativePath;
            }

            // 确保目录存在
            File parentDir = file.getParentFile();
            if (!parentDir.exists()) {
                parentDir.mkdirs();
            }

            // 调用百度TTS API
            boolean success = callBaiduTTS(text, accessToken, fullPath, voiceType, speed, pitch, volume);

            return success ? relativePath : null;

        } catch (Exception e) {
            System.err.println("语音合成失败: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String textToSpeech(String text) {
        return textToSpeech(text, "1", 5, 5, 5); // 默认参数：度小宇，中语速，中音调，中音量
    }

    @Override
    public Map<String, Object> getVoiceConfig() {
        Map<String, Object> config = new HashMap<>();
        config.put("enabled", ttsEnabled);
        config.put("maxTextLength", 1024);

        // 语音类型配置
        Map<String, String> voiceTypes = new HashMap<>();
        voiceTypes.put("1", "度小宇（男声）");
        voiceTypes.put("0", "度小美（女声）");
        voiceTypes.put("3", "度逍遥（男声）");
        voiceTypes.put("4", "度丫丫（女声，童声）");
        voiceTypes.put("5003", "度逍遥（精品男声）");
        voiceTypes.put("5118", "度小鹿（精品女声）");
        voiceTypes.put("106", "度博文（精品男声）");
        voiceTypes.put("110", "度小童（精品童声）");
        config.put("voiceTypes", voiceTypes);

        // 参数范围
        config.put("speedRange", "0-15（语速）");
        config.put("pitchRange", "0-15（音调）");
        config.put("volumeRange", "0-15（音量）");

        return config;
    }

    @Override
    public boolean deleteVoiceFile(String filePath) {
        try {
            String fullPath = RuoYiConfig.getUploadPath() + filePath;
            File file = new File(fullPath);
            return file.exists() && file.delete();
        } catch (Exception e) {
            System.err.println("删除语音文件失败: " + e.getMessage());
            return false;
        }
    }

    /**
     * 标记语音文件为不活跃状态（软删除）
     * @param filePath 文件路径，如：/upload/voice/filename.mp3
     * @return 是否操作成功
     */
    public boolean markVoiceFileAsInactive(String filePath) {
        try {
            // 查找对应的数据库记录
            VoiceFiles query = new VoiceFiles();
            query.setFilePath(filePath);
            query.setIsActive(1);

            List<VoiceFiles> voiceFilesList = voiceFilesService.selectVoiceFilesList(query);

            if (voiceFilesList.isEmpty()) {
                System.err.println("未找到对应的语音文件记录: " + filePath);
                return false;
            }

            // 将所有匹配的记录标记为不活跃
            boolean success = true;
            for (VoiceFiles voiceFile : voiceFilesList) {
                voiceFile.setIsActive(0);
                int updateResult = voiceFilesService.updateVoiceFiles(voiceFile);
                if (updateResult <= 0) {
                    success = false;
                    System.err.println("更新语音文件记录失败: " + voiceFile.getId());
                }
            }

            System.out.println("成功标记 " + voiceFilesList.size() + " 个语音文件记录为不活跃状态: " + filePath);
            return success;

        } catch (Exception e) {
            System.err.println("标记语音文件为不活跃状态失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public int cleanExpiredVoiceFiles(int days) {
        int count = 0;
        try {
            String voiceDir = RuoYiConfig.getUploadPath() + VOICE_DIR;
            File dir = new File(voiceDir);

            if (!dir.exists() || !dir.isDirectory()) {
                return count;
            }

            long expireTime = System.currentTimeMillis() - (days * 24L * 60L * 60L * 1000L);

            File[] files = dir.listFiles((file) ->
                file.isFile() && file.getName().endsWith(".mp3") && file.lastModified() < expireTime
            );

            if (files != null) {
                for (File file : files) {
                    if (file.delete()) {
                        count++;
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("清理过期语音文件失败: " + e.getMessage());
        }
        return count;
    }

    /**
     * 生成站点播报语音（先查数据库，不存在再生成）
     *
     * @param stationId 站点ID
     * @param text 播报文本
     * @param announcementType 播报类型
     * @param voiceType 语音类型
     * @param speed 语速
     * @param pitch 音调
     * @param volume 音量
     * @return 语音文件相对路径
     */
    public String generateStationVoice(Long stationId, String text, String announcementType,
                                     String voiceType, Integer speed, Integer pitch, Integer volume) {
        if (!ttsEnabled || text == null || text.trim().isEmpty()) {
            return null;
        }

        try {
            // 1. 先从数据库查找是否已有该站点的播报语音
            VoiceFiles existingVoice = findExistingStationVoice(stationId, announcementType);
            if (existingVoice != null) {
                // 检查文件是否存在
                String fullPath = RuoYiConfig.getUploadPath() + existingVoice.getFilePath();
                File file = new File(fullPath);
                if (file.exists()) {
                    System.out.println("找到已存在的站点播报语音: " + existingVoice.getFilePath());
                    return existingVoice.getFilePath();
                } else {
                    // 文件不存在，删除数据库记录
                    voiceFilesService.deleteVoiceFilesById(existingVoice.getId());
                }
            }

            // 2. 数据库中没有或文件已丢失，重新生成
            String filePath = textToSpeech(text, voiceType, speed, pitch, volume);
            if (filePath == null) {
                return null;
            }

            // 3. 保存站点播报记录到数据库
            saveVoiceFileToDatabase(stationId, filePath, text, announcementType,
                                  voiceType, speed, pitch, volume);

            return filePath;

        } catch (Exception e) {
            System.err.println("生成站点播报语音失败: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 查找已存在的站点播报语音
     */
    private VoiceFiles findExistingStationVoice(Long stationId, String announcementType) {
        try {
            VoiceFiles query = new VoiceFiles();
            query.setStationId(stationId);
            query.setLanguageId(1L); // 中文
            query.setAnnouncementTypeId(getAnnouncementTypeId(announcementType));
            query.setIsActive(1);

            List<VoiceFiles> list = voiceFilesService.selectVoiceFilesList(query);
            return list.isEmpty() ? null : list.get(0);

        } catch (Exception e) {
            System.err.println("查找站点播报语音失败: " + e.getMessage());
            return null;
        }
    }

    /**
     * 保存语音文件记录到数据库
     */
    private void saveVoiceFileToDatabase(Long stationId, String filePath, String originalText,
                                       String announcementType, String voiceType,
                                       Integer speed, Integer pitch, Integer volume) {
        try {
            VoiceFiles voiceFile = new VoiceFiles();
            voiceFile.setStationId(stationId);
            voiceFile.setLanguageId(1L); // 默认中文，ID=1
            voiceFile.setAnnouncementTypeId(getAnnouncementTypeId(announcementType));

            // 从文件路径中提取文件名
            String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
            voiceFile.setFileName(fileName);
            voiceFile.setFilePath(filePath);

            // 获取文件大小
            String fullPath = RuoYiConfig.getUploadPath() + filePath;
            File file = new File(fullPath);
            if (file.exists()) {
                voiceFile.setFileSize(file.length());
            }

            // 映射语音参数
            voiceFile.setVoiceSpeed(mapSpeedToEnum(speed));
            voiceFile.setVoiceTone(mapVoiceTypeToTone(voiceType));
            voiceFile.setTtsModelVersion("baidu-tts-v1");

            // 生成文件校验和
            if (file.exists()) {
                voiceFile.setChecksum(generateChecksum(file));
            }

            voiceFile.setIsActive(1);

            voiceFilesService.insertVoiceFiles(voiceFile);

        } catch (Exception e) {
            System.err.println("保存语音文件记录失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 获取报站类型ID
     */
    private Long getAnnouncementTypeId(String announcementType) {
        switch (announcementType) {
            case "arrival":
                return 2L; // "已到达"
            case "departure":
                return 2L; // 使用相同的ID，或者需要新增类型
            case "next":
                return 1L; // "即将到站"
            case "custom":
                return 1L; // 自定义文本，归类为即将到站类型
            default:
                return 1L; // 默认为即将到站
        }
    }

    /**
     * 将语速映射到枚举值
     */
    private String mapSpeedToEnum(Integer speed) {
        if (speed == null) return "normal";
        if (speed <= 3) return "slow";
        if (speed >= 10) return "fast";
        return "normal";
    }

    /**
     * 将语音类型映射到语调枚举
     */
    private String mapVoiceTypeToTone(String voiceType) {
        switch (voiceType) {
            case "0": // 度小美
                return "gentle";
            case "3": // 度逍遥
                return "formal";
            case "4": // 度丫丫
                return "gentle";
            default: // 度小宇
                return "standard";
        }
    }

    /**
     * 生成文件MD5校验和
     */
    private String generateChecksum(File file) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[8192];
            try (FileInputStream fis = new FileInputStream(file)) {
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    md.update(buffer, 0, bytesRead);
                }
            }

            StringBuilder sb = new StringBuilder();
            for (byte b : md.digest()) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取百度API访问令牌
     */
    private String getAccessToken() {
        if (apiKey == null || apiKey.isEmpty() || secretKey == null || secretKey.isEmpty()) {
            return null;
        }

        String cacheKey = apiKey + ":" + secretKey;

        // 检查缓存
        if (tokenCache.containsKey(cacheKey)) {
            Long expireTime = tokenExpireCache.get(cacheKey);
            if (expireTime != null && System.currentTimeMillis() < expireTime) {
                return tokenCache.get(cacheKey);
            }
        }

        try {
            String urlParams = String.format("grant_type=client_credentials&client_id=%s&client_secret=%s",
                apiKey, secretKey);

            URL url = new URL(TOKEN_API_URL + "?" + urlParams);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(10000);

            int responseCode = connection.getResponseCode();
            if (responseCode == 200) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
                reader.close();

                // 简单解析JSON获取access_token
                String responseStr = response.toString();
                String tokenPrefix = "\"access_token\":\"";
                int tokenStart = responseStr.indexOf(tokenPrefix);
                if (tokenStart >= 0) {
                    tokenStart += tokenPrefix.length();
                    int tokenEnd = responseStr.indexOf("\"", tokenStart);
                    if (tokenEnd > tokenStart) {
                        String accessToken = responseStr.substring(tokenStart, tokenEnd);

                        // 缓存token（有效期30天减去1小时的安全边际）
                        long expireTime = System.currentTimeMillis() + (29 * 24 * 60 * 60 * 1000L);
                        tokenCache.put(cacheKey, accessToken);
                        tokenExpireCache.put(cacheKey, expireTime);

                        return accessToken;
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("获取访问令牌失败: " + e.getMessage());
        }

        return null;
    }

    /**
     * 调用百度TTS API
     */
    private boolean callBaiduTTS(String text, String accessToken, String outputPath,
                                String voiceType, Integer speed, Integer pitch, Integer volume) {
        try {
            URL url = new URL(TTS_API_URL);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setConnectTimeout(30000);
            connection.setReadTimeout(60000);
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

            // 构建POST参数
            StringBuilder postData = new StringBuilder();
            postData.append("tex=").append(URLEncoder.encode(URLEncoder.encode(text, "UTF-8"), "UTF-8")); // 2次URL编码
            postData.append("&lan=zh");
            postData.append("&cuid=smartbus_tts_").append(System.currentTimeMillis());
            postData.append("&ctp=1");
            postData.append("&tok=").append(accessToken);
            postData.append("&aue=3"); // MP3格式
            postData.append("&per=").append(voiceType != null ? voiceType : "1");
            postData.append("&spd=").append(speed != null ? speed : 5);
            postData.append("&pit=").append(pitch != null ? pitch : 5);
            postData.append("&vol=").append(volume != null ? volume : 5);

            // 发送请求
            OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
            writer.write(postData.toString());
            writer.flush();
            writer.close();

            int responseCode = connection.getResponseCode();
            if (responseCode == 200) {
                String contentType = connection.getContentType();
                if (contentType != null && contentType.contains("audio")) {
                    // 保存音频文件
                    try (InputStream inputStream = connection.getInputStream();
                         FileOutputStream outputStream = new FileOutputStream(outputPath)) {

                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                        return true;
                    }
                } else {
                    // 错误响应
                    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                    StringBuilder errorResponse = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        errorResponse.append(line);
                    }
                    reader.close();
                    System.err.println("TTS API错误响应: " + errorResponse.toString());
                }
            } else {
                System.err.println("TTS API响应码: " + responseCode);
            }
        } catch (Exception e) {
            System.err.println("调用百度TTS API失败: " + e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 生成语音文件名
     */
    private String generateFileName(String text, String voiceType, Integer speed, Integer pitch, Integer volume) {
        try {
            String input = text + "_" + voiceType + "_" + speed + "_" + pitch + "_" + volume;
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hashBytes = md.digest(input.getBytes(StandardCharsets.UTF_8));

            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }

            String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            return timestamp + "_" + sb.toString() + ".mp3";
        } catch (Exception e) {
            // 如果MD5失败，使用时间戳
            return "tts_" + System.currentTimeMillis() + ".mp3";
        }
    }
}
